package com.evildog.workspace.system.application.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.evildog.workspace.common.object.PageQuery;
import com.evildog.workspace.common.result.PagedResult;
import com.evildog.workspace.common.util.WsAssert;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evildog.workspace.dependencies.mybatis.base.TableLinkService;
import com.evildog.workspace.dependencies.mybatis.object.LinkIdDTO;
import com.evildog.workspace.dependencies.mybatis.object.QueryConditionDTO;
import com.evildog.workspace.dependencies.mybatis.util.QueryConditionUtils;
import com.evildog.workspace.security.jwt.util.SecurityUtil;
import com.evildog.workspace.system.application.bo.UserBO;
import com.evildog.workspace.system.application.bo.UserPageBO;
import com.evildog.workspace.system.application.consts.UserConst;
import com.evildog.workspace.system.application.convert.UserConvert;
import com.evildog.workspace.system.application.dto.UserCreateDTO;
import com.evildog.workspace.system.application.dto.UserDTO;
import com.evildog.workspace.system.application.dto.UserPageDTO;
import com.evildog.workspace.system.application.dto.UserUpdatePassDTO;
import com.evildog.workspace.system.application.entity.User;
import com.evildog.workspace.system.application.entity.UserPageDO;
import com.evildog.workspace.system.application.entity.UserUserGroupLink;
import com.evildog.workspace.system.application.enums.SystemCustomExceptionDesc;
import com.evildog.workspace.system.application.enums.UserStatusEnum;
import com.evildog.workspace.system.application.mapper.UserGroupMapper;
import com.evildog.workspace.system.application.mapper.UserMapper;
import com.evildog.workspace.system.application.service.UserService;
import com.evildog.workspace.system.application.service.UserUserGroupLinkService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private UserUserGroupLinkService userUserGroupLinkService;
    private TableLinkService tableLinkService;
    private UserGroupMapper userGroupMapper;

    @Override
    public PagedResult<UserPageBO> getPageList(PageQuery query, List<QueryConditionDTO> queryConditions, UserPageDTO pageDTO) {
        IPage<UserPageDO> page = this.baseMapper.getPageList(
                QueryConditionUtils.getPagination(query),
                queryConditions,
                UserConvert.INSTANCE.convert(pageDTO));
        return UserConvert.INSTANCE.convert(page);
    }

    @Override
    public UserBO getDetailById(Long id) {
        User user = this.getById(id);
        Assert.notNull(user, "User is null!");
        return UserConvert.INSTANCE.convert(user);
    }

    @Override
    public boolean save(UserCreateDTO param) {
        String password = param.getPassword();
        String confirmPassword = param.getConfirmPassword();

        WsAssert.isTrue(Objects.equals(password, confirmPassword), SystemCustomExceptionDesc.TWO_PASSWORD_NOT_EQ);

        String account = param.getAccount();
        checkSameAccount(account);

        User user = new User()
                .setAccount(account)
                .setPassword(DigestUtil.bcrypt(password))
                .setRemark(param.getRemark())
                .setAvatar(UserConst.DEFAULT__AVATAR);

        return save(user);
    }


    @Override
    public UserBO getAccount(String account, String password) {
        User userDO = getOne(Wrappers.<User>lambdaQuery().eq(User::getAccount, account));
        checkUserValid(userDO);

        UserBO userBO = null;
        if (DigestUtil.bcryptCheck(password, userDO.getPassword())) {
            userBO = UserConvert.INSTANCE.convert(userDO);
        }

        return userBO;
    }

    @Override
    public UserBO getAccount(Long userId) {
        User userDO = getById(userId);
        checkUserValid(userDO);
        return UserConvert.INSTANCE.convert(userDO);
    }


    @Override
    public boolean updateById(UserDTO dto) {
        Long userId = SecurityUtil.getUserId();

        checkSameAccount(dto.getAccount());

        User oldUser = getById(userId);
        checkUserValid(oldUser);

        Assert.notNull(oldUser, "user is null!");
        User newUser = UserConvert.INSTANCE.convert(dto);
        newUser.setId(userId);

        return updateById(newUser);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateUserGroup(Long id, LinkIdDTO param) {
        User oldUser = getById(id);
        Assert.notNull(oldUser, "user is null!");

        tableLinkService.removeByLinkId(userUserGroupLinkService, UserUserGroupLink::getUserId, id);

        List<Long> userGroupIds = param.getLinkIds();

        List<UserUserGroupLink> links = new ArrayList<>();
        userGroupIds.forEach(userGroupId ->
                links.add(new UserUserGroupLink()
                        .setUserGroupId(userGroupId)
                        .setUserId(id)
                )
        );

        return userUserGroupLinkService.saveBatch(links);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean remove(Long id) {
        if (removeById(id)) {
            // 删除与user group的关联
            tableLinkService.removeByLinkId(userUserGroupLinkService, UserUserGroupLink::getUserId, id);

        }
        return true;
    }

    @Override
    public boolean updatePassword(UserUpdatePassDTO dto) {
        Long userId = SecurityUtil.getUserId();
        User oldUser = getById(userId);

        checkUserValid(oldUser);

        // 检查原密码是否正确
        String originalPassword = dto.getOriginalPassword();
        WsAssert.isTrue(DigestUtil.bcryptCheck(originalPassword, oldUser.getPassword()),SystemCustomExceptionDesc.USER_DISABLED);

        String newPassword = dto.getNewPassword();
        String confirmPassword = dto.getConfirmPassword();
        WsAssert.isTrue(Objects.equals(newPassword, confirmPassword), SystemCustomExceptionDesc.TWO_PASSWORD_NOT_EQ);

        User updateUserDO = new User()
                .setId(userId)
                .setPassword(newPassword);

        return updateById(updateUserDO);
    }


    private void checkUserValid(User userDO) {
        WsAssert.notNull(userDO, SystemCustomExceptionDesc.USER_NOT_FOUND);
        WsAssert.isTrue(Objects.equals(UserStatusEnum.ABLE_USER_ENUM.getStatus(), userDO.getStatus()), SystemCustomExceptionDesc.USER_DISABLED);
    }

    private void checkSameAccount(String account) {
        int count = count(Wrappers.<User>lambdaQuery()
                .eq(User::getAccount, account));

        WsAssert.isTrue(count == 0, SystemCustomExceptionDesc.DUPLICATION_ACCOUNT);
    }
}
